package com.hnblc.blcwms.rest.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hnblc.blcwms.common.constant.StringPool;
import com.hnblc.blcwms.common.exceptions.GeneralException;
import com.hnblc.blcwms.common.exceptions.ManualValidationFailException;
import com.hnblc.blcwms.common.interaction.Response;
import com.hnblc.blcwms.common.utils.encrypt.BASE64;
import com.hnblc.blcwms.common.utils.encrypt.MD5Util;
import com.hnblc.blcwms.persistent.interfaces.client.entity.ClientAuth;
import com.hnblc.blcwms.persistent.interfaces.client.entity.ClientInfo;
import com.hnblc.blcwms.persistent.interfaces.client.service.IClientInfoService;
import com.hnblc.blcwms.rest.dto.restInteraction.EncryptRequest;
import com.hnblc.blcwms.rest.dto.restInteraction.ReadableRequest;
import com.hnblc.blcwms.serviceapi.api.dto.WareHouseOperate;
import com.hnblc.blcwms.serviceapi.api.enums.result.APIResultEnum;
import com.hnblc.blcwms.serviceapi.api.group.BaseGroup;
import com.hnblc.blcwms.serviceapi.api.group.custom.CustomsGroup;
import com.hnblc.blcwms.serviceapi.api.group.owner.OwnerGroup;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.Set;

@RestController
public class BaseRestAPI {

    private static final DecimalFormat WAREHOUSE_FORMATTER = new DecimalFormat("000");
    @Autowired
    IClientInfoService clientInfoService;
    @Autowired
    Validator customValidator;
    @Autowired
    ObjectMapper objectMapper;

    Logger logger = LoggerFactory.getLogger(BaseRestAPI.class);

    protected <T> Response<T> preProcess(EncryptRequest message, HttpServletRequest request, Class<BaseGroup> validationGroupClass) throws IOException {
        //测试打印原始报文
        String jsonString = IOUtils.toString(request.getInputStream(), Charset.defaultCharset());
        logger.debug("***********************************create Purchase test start***************************\n"+jsonString+"\n*********************end*************************");

        //解析业务数据 从BASE64字符串解析为JSON字符串，再解析为业务对象
        T businessData = null;
        try {
            businessData = objectMapper.readValue(new String(BASE64.decryptBASE64(message.getBusinessData()), StringPool.CHARSET_UTF8),new TypeReference<T>() { });
        } catch (Exception e) {
            e.printStackTrace();
        }
        //校验客户端是否存在
        ClientInfo clientInfo = clientInfoService.getOne(new QueryWrapper<ClientInfo>().eq("client_id",message.getClientId()));

        if (clientInfo==null){
            return new Response<>(APIResultEnum.ERROR_CLIENT_NOT_FOUND);
        }

        //验签
        try {
            if (!this.signatureValidate(message, clientInfo)) {
                return new Response<>(APIResultEnum.ERROR_SIGNATURE_FAIL);
            }
        } catch (Exception e) {
            logger.error("接口处理验签异常", e);
            throw new GeneralException(e);
        }

        //简单验证
        this.validate(businessData, validationGroupClass);

        return new Response<>(APIResultEnum.SUCCESS);
    }







    protected boolean signatureValidate(ReadableRequest<? extends WareHouseOperate> request, ClientInfo clientInfo) throws IOException {
//        TreeMap<String,String> seed  = new TreeMap<>();
//        objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY,true);
//        Map seed = objectMapper.readValue(objectMapper.writeValueAsString(request), TreeMap.class);
//        seed.remove("signature");
//        Map<String,String> map = BeanUtils.beanToStrMap(request);
//        seed.putAll(map);
//        seed.put("business_data",objectMapper.writeValueAsString(request.getBusinessData()));
        System.out.println("********************JSON PRINT************************\n"+request.getBusinessData()+"\n*****************************************");
//        StringBuffer sourceBuffer = new StringBuffer();
//        sourceBuffer.append(request.getBusinessData())
//                .append(request.getTimestamp())
//                .append(clientInfo.getClientKey());

        //签名算法 ：MD5(ToUpper(MD5(ToUpper(key1=value1&key2=value2…))+timpstamp+key))
//        System.out.println(request.getSignature());
//        System.out.println(sourceStr.substring(0,sourceStr.length()-1));
//        System.out.println(MD5Util.md5Encode(sourceStr.substring(0,sourceStr.length()-1).toUpperCase()));
//        System.out.println(MD5Util.md5Encode(sourceStr.substring(0,sourceStr.length()-1).toUpperCase()).concat(request.getTimestamp()).concat(clientInfo.getClientKey()).toUpperCase());
//        System.out.println(MD5Util.md5Encode(MD5Util.md5Encode(sourceStr.substring(0,sourceStr.length()-1).toUpperCase()).concat(request.getTimestamp()).concat(clientInfo.getClientKey()).toUpperCase()));
//        return request.getSignature().equalsIgnoreCase(MD5Util.md5Encode(sourceBuffer.toString()));
        return true;
    }


    /**
     * contentType=application/json,时，明文加签不便，使用
     * @param request
     * @param clientInfo
     * @return
     * @throws IOException
     */
    protected boolean signatureValidate(EncryptRequest request, ClientInfo clientInfo) throws IOException {

        StringBuffer sourceBuffer = new StringBuffer();
        sourceBuffer.append(request.getBusinessData())
                .append(request.getTimestamp())
                .append(clientInfo.getClientKey());
        return request.getSignature().equalsIgnoreCase(MD5Util.md5Encode(sourceBuffer.toString()));
    }

    protected String signature(String businessData,String timpstamp,String key) throws UnsupportedEncodingException {
        return MD5Util.md5Encode(businessData+timpstamp+key);
    }



    protected ClientAuth buildAuth(ReadableRequest<? extends WareHouseOperate> request) {
        ClientAuth ca = new ClientAuth();
        ca.setClientId(request.getClientId());
        ca.setEnterpriseNo(request.getBusinessData().getEnterpriseNo());
        ca.setWarehouseNo(request.getBusinessData().getWarehouseNo());
        ca.setOwnerNo(request.getBusinessData().getOwnerNo());
        ca.setCustomNo(request.getBusinessData().getCustomNo());
        return ca;
    }

    protected ClientAuth buildAuth(EncryptRequest request,@Validated({CustomsGroup.class}) WareHouseOperate operate) {
        ClientAuth ca = new ClientAuth();
        ca.setClientId(request.getClientId());
        ca.setEnterpriseNo(operate.getEnterpriseNo());
        ca.setWarehouseNo(operate.getWarehouseNo());
        ca.setOwnerNo(operate.getOwnerNo());
        ca.setCustomNo(operate.getCustomNo());
        return ca;
    }

    protected ClientAuth buildOwnerAuth(EncryptRequest request,@Validated({OwnerGroup.class}) WareHouseOperate operate) {
        ClientAuth ca = new ClientAuth();
        ca.setClientId(request.getClientId());
        ca.setEnterpriseNo(operate.getEnterpriseNo());
        ca.setOwnerNo(operate.getOwnerNo());
        //货主权限，仓别专用位
        ca.setWarehouseNo("N");
        //货主权限，客户专用位
        ca.setCustomNo("N");
        return ca;
    }

    /**
     * 根据业务对象和对应的验证组验证业务对象
     * @param businessObj 业务对象
     * @param group   验证分组接口
     * @param <T>  业务对象泛型
     */
    protected <T> void validate(T businessObj,Class<? extends BaseGroup> group) {
        Set<ConstraintViolation<T>> validateSet = customValidator
                .validate(businessObj,group);
        if (validateSet!=null && !((Set) validateSet).isEmpty()) {
            StringBuffer messageBuffer = new StringBuffer();
            for (ConstraintViolation<T> constraintViolation : validateSet) {
                messageBuffer.append("[")
                        .append(constraintViolation.getPropertyPath().toString())
                        .append("]")
                        .append(constraintViolation.getMessage())
                        .append(";");
            }
            throw new ManualValidationFailException(messageBuffer.deleteCharAt(messageBuffer.length()-1).toString());
        }
    }
}
